package com.xunan.boot.service;

import cn.hutool.core.util.IdUtil;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.model.*;
import com.qcloud.cos.utils.IOUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * 腾讯云COS操作
 *
 * @author xn
 * @date 2022/6/20 15:56
 */
@Service
@Slf4j
public class COSFilesService {

    /**
     * 指定文件将要存放的存储桶
     */
    @Value("${tencent.yun.bucketName}")
    private String bucketName;

    /**
     * COS路径-访问域名
     */
    @Value("${tencent.yun.url}")
    private String COSUrl;

    /**
     * COS客户端
     */
    @Resource
    COSClient cosClient;

    /**
     * 上传文件到COS
     *
     * @param file 上传文件
     * @return 该文件位于COS上的路径
     */
    public String fileUpload(MultipartFile file) {

        //文件名称 为了保证文件名称唯一，此处使用uuid来设置文件名称
        String filename = IdUtil.fastSimpleUUID() + "_" + file.getOriginalFilename();

        System.out.println(filename);

        //目录的生成 当前日期
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String date = dateFormat.format(new Date());

        // 指定文件上传到 COS 上的路径，即对象键。例如对象键为folder/picture.jpg，则表示将文件 picture.jpg 上传到 folder 路径下
        //此处目录规则为：当前日期/文件名称
        String key = date + "/" + filename;
        //将multipartFile转为file
        File toFile = multipartFileToFile(file);
        //COS上传操作
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, toFile);
        PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
        log.info("putObjectResult:" + putObjectResult);

        //删除本地上传资源
        deleteTempFile(multipartFileToFile(file));

        //下载链接
        String fileDownloadLink = COSUrl + "/" + key;
        System.out.println("fileDownloadLink: " + fileDownloadLink);
        /*
        //为文件设置一个过期时间
        Date expiration = new Date(new Date().getTime() + 5 * 60 * 10000);

        GeneratePresignedUrlRequest req =
                new GeneratePresignedUrlRequest(bucketName, key, HttpMethodName.GET);
        // 设置签名过期时间(可选), 若未进行设置, 则默认使用 ClientConfig 中的签名过期时间(1小时)
        // 可以设置任意一个未来的时间，推荐是设置 10 分钟到 3 天的过期时间
        req.setExpiration(expiration);

        URL url = cosClient.generatePresignedUrl(req);

        //关闭客户端
        cosClient.shutdown();

        //拼接文件地址
        StringBuffer stringBuffer = new StringBuffer()
                .append(url.getProtocol())
                .append("://")
                .append(url.getHost())
                .append(url.getPath());*/

        //该文件位于COS上的路径
        //putObjectResult.toString() == key
        return putObjectResult.toString();
    }

    /**
     * 查询存储桶中对象列表
     *
     * @param PrefixPath 腾讯云COS对应桶下某个文件夹
     */
    public void selectFiles(String PrefixPath) {

        // Bucket的命名格式为 BucketName-APPID ，此处填写的存储桶名称必须为此格式
        //String bucketName = "examplebucket-1250000000";
        ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
        // 设置bucket名称
        listObjectsRequest.setBucketName(bucketName);
        // prefix表示列出的object的key以prefix开始
        listObjectsRequest.setPrefix(PrefixPath);
        // deliter表示分隔符
        // 设置为/表示列出当前目录下的object, 设置为空表示列出所有的object
        listObjectsRequest.setDelimiter("");
        // 设置最大遍历出多少个对象, 一次listobject最大支持1000
        listObjectsRequest.setMaxKeys(1000);
        ObjectListing objectListing = null;

        log.info("");

        do {
            try {
                objectListing = cosClient.listObjects(listObjectsRequest);
            } catch (CosServiceException e) {
                e.printStackTrace();
                return;
            } catch (CosClientException e) {
                e.printStackTrace();
                return;
            }
            // common prefix表示表示被delimiter截断的路径, 如delimter设置为/, common prefix则表示所有子目录的路径
            List<String> commonPrefixs = objectListing.getCommonPrefixes();
            for (String commonPrefix : commonPrefixs) {
                log.info("commonPrefix: " + commonPrefix);
            }
            // object summary表示所有列出的object列表
            List<COSObjectSummary> cosObjectSummaries = objectListing.getObjectSummaries();
            for (COSObjectSummary cosObjectSummary : cosObjectSummaries) {
                // 文件的路径key
                String key = cosObjectSummary.getKey();
                // 文件的etag
                String etag = cosObjectSummary.getETag();
                // 文件的长度
                long fileSize = cosObjectSummary.getSize();
                // 文件的存储类型
                String storageClasses = cosObjectSummary.getStorageClass();
                log.info("key: " + key);
                log.info("fileSize: " + fileSize);
                log.info("");
            }

            String nextMarker = objectListing.getNextMarker();
            log.info("nextMarker: " + nextMarker);
            listObjectsRequest.setMarker(nextMarker);
            log.info("listObjectsRequest: " + listObjectsRequest);
            log.info("");
        } while (objectListing.isTruncated());
    }

    /**
     * 下载文件
     * 参考文档：https://cloud.tencent.com/document/product/436/65937#.E4.B8.8B.E8.BD.BD.E5.AF.B9.E8.B1.A12  的最后
     *
     * @param COSKey 指定文件在 COS 上的路径，即对象键。例如对象键为folder/picture.jpg，则表示下载的文件 picture.jpg 在 folder 路径下
     * @return 下载链接
     */
    public String downloadFile(String COSKey) {
        // Bucket的命名格式为 BucketName-APPID ，此处填写的存储桶名称必须为此格式
        //String bucketName = "examplebucket-1250000000";
        // 指定文件在 COS 上的路径，即对象键。例如对象键为folder/picture.jpg，则表示下载的文件 picture.jpg 在 folder 路径下
        //String COSKey = "exampleobject";

        GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, COSKey);
        COSObjectInputStream cosObjectInput = null;
        String downloadLink = null;

        try {
            COSObject cosObject = cosClient.getObject(getObjectRequest);
            cosObjectInput = cosObject.getObjectContent();

            System.out.println(cosObjectInput.getHttpRequest());//GET https://xxxxx HTTP/1.1
            URI downloadUri = cosObjectInput.getHttpRequest().getURI();
            downloadLink = String.valueOf(downloadUri);
            System.out.println(downloadLink);
        } catch (CosServiceException e) {
            e.printStackTrace();
        } catch (CosClientException e) {
            e.printStackTrace();
        }

        // 处理下载到的流
        // 这里是直接读取，按实际情况来处理
        byte[] bytes = null;
        try {
            bytes = IOUtils.toByteArray(cosObjectInput);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 用完流之后一定要调用 close()
            try {
                cosObjectInput.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // 在流没有处理完之前，不能关闭 cosClient
        // 确认本进程不再使用 cosClient 实例之后，关闭之
        cosClient.shutdown();

        return downloadLink;
    }

    /**
     * 删除文件
     *
     * @param COSKey 指定文件在 COS 上的路径，即对象键。例如对象键为folder/picture.jpg，则表示下载的文件 picture.jpg 在 folder 路径下
     * @return 是否成功
     */
    public Boolean deleteFile(String COSKey) {
        // Bucket的命名格式为 BucketName-APPID ，此处填写的存储桶名称必须为此格式
        //String bucketName = "examplebucket-1250000000";
        // 指定被删除的文件在 COS 上的路径，即对象键。例如对象键为folder/picture.jpg，则表示删除位于 folder 路径下的文件 picture.jpg
        //String COSKey = "exampleobject";
        cosClient.deleteObject(bucketName, COSKey);
        return true;
    }

    /**
     * 将multipartFile转为file
     *
     * @param file
     * @return java.io.File
     **/
    private File multipartFileToFile(MultipartFile file) {

        File toFile = null;
        if (file.equals("") || file.getSize() <= 0) {
            file = null;
        } else {
            try {
                InputStream ins = null;
                ins = file.getInputStream();
                toFile = new File(file.getOriginalFilename());
                //获取流文件
                inputStreamToFile(ins, toFile);
                ins.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return toFile;
    }

    /**
     * 获取流文件
     *
     * @param ins
     * @param file
     */
    private static void inputStreamToFile(InputStream ins, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 删除本地临时文件
     *
     * @param file
     */
    public static void deleteTempFile(File file) {
        if (file != null) {
            File del = new File(file.toURI());
            del.delete();
        }
    }

}
